package com.ydj.app.service;

import com.ydj.app.entity.Kol;
import com.ydj.app.entity.dao.KolDao;
import com.ydj.app.entity.query.KolQuery;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ydj.app.WebConst;
import com.ydj.app.entity.CmsAccount;
import com.ydj.app.entity.WebUser;
import com.ydj.app.entity.dao.CmsAccountDao;
import com.ydj.app.entity.dao.WebUserDao;
import com.ydj.app.entity.query.CmsAccountQuery;
import com.ydj.app.entity.query.WebUserQuery;
import com.ydj.app.enumerate.MemberLevelEnum;
import com.ydj.app.protocol.http.oldcms.CmsCommonGetProfileRes;
import com.ydj.app.protocol.http.oldcms.CmsCommonLoginReq;
import com.ydj.app.protocol.http.oldcms.CmsCommonLoginRes;
import com.ydj.app.protocol.http.web.*;
import com.ydj.app.protocol.redis.RdsCmsLoginInfo;
import com.ydj.app.protocol.redis.RdsWebLoginInfo;
import com.ydj.framework.exception.CommonException;
import com.ydj.framework.exception.VerifyTokenException;
import com.ydj.framework.redis.ExtRedisTemplate;
import com.ydj.framework.redis.RedisKey;
import com.ydj.framework.third.cos.CosManager;
import com.ydj.framework.third.email.EmailManager;
import com.ydj.framework.third.oss.OssPath;
import com.ydj.framework.third.tencentsms.TencentSmsManager;
import com.ydj.framework.third.wxpay.WxCommonManager;
import com.ydj.framework.third.wxpay.protocol.WxGetOpenIdByCodeRes;
import com.ydj.framework.third.wxpay.protocol.WxGetUserPhoneNumberRes;
import com.ydj.framework.utils.CommonUtils;
import com.ydj.framework.utils.DateUtils;
import com.ydj.framework.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 公用服务
 */
@Slf4j
@Service
public class CommonService {
    @Autowired
    private TencentSmsManager tencentSmsManager;
    @Autowired
    EmailManager emailManager;
    @Autowired
    WxCommonManager wxCommonManager;
    @Autowired
    CosManager cosManager;
    @Autowired
    ExtRedisTemplate redisTemplate;
    @Autowired
    CmsAccountDao cmsAccountDao;
    @Autowired
    WebUserDao webUserDao;
    @Autowired
    KolDao kolDao;

    /**
     * CMS登录
     */
    @Transactional(rollbackOn = Exception.class)
    public CmsCommonLoginRes cmsLogin(HttpServletRequest request, CmsCommonLoginReq req) throws CommonException {
        // 查询用户
        CmsAccount account = new CmsAccountQuery(cmsAccountDao)
                .username(req.getUsername())
                .password(req.getPassword())
                .single();
        CommonException.throwExceptionIfNull(account, "账号或密码错误");

        // 分配token，并且保存到redis
        String token = CommonUtils.getToken();
        RdsCmsLoginInfo loginInfo = new RdsCmsLoginInfo()
                .setAccountId(account.getId())
                .setName(account.getUsername());

        // 保存用户的登录信息
        account.setLastLoginIp(IpUtils.getIpAddr(request))
                .setLastLoginTime(new Date());
        cmsAccountDao.saveOrUpdate(account);

        redisTemplate.opsForValue().set(RedisKey.getCmsToken(token), loginInfo, 7, TimeUnit.DAYS);

        return new CmsCommonLoginRes()
                .setToken(token);
    }

    /**
     * 获取用户信息
     */
    public CmsCommonGetProfileRes cmsGetProfile(CmsAccount account) {
        return new CmsCommonGetProfileRes()
                .setUsername(account.getUsername());
    }

    /**
     * 微信端非授权登录
     */
    public WebCommonWxLoginRes webWxLogin(WebCommonWxLoginReq req) throws CommonException {
        WxGetOpenIdByCodeRes wxGetOpenIdByCodeRes = wxCommonManager.getOpenIdByCode(req.getCode());
        CommonException.throwExceptionIfNull(wxGetOpenIdByCodeRes, "获取用户信息失败");
        String openId = wxGetOpenIdByCodeRes.getOpenid();
        String unionId = wxGetOpenIdByCodeRes.getUnionid();

        // 查询账号
        WebUser user = new WebUserQuery(webUserDao)
                .openId(openId)
                .single();
        boolean newUser = (user == null);
        if (user == null) {
            user = new WebUser()
                    .setOpenId(openId)
                    .setUnionId(unionId)
                    .setLevel(MemberLevelEnum.会员)
                    .setExpTime(DateUtils.stringToDateWithoutException("2099-01-01 00:00:00"))
                    .setFreeChatCount(WebConst.FREE_CHAT_TIME)
                    .setFreeForecastPersonalCount(WebConst.FREE_RWA_FORECAST_PERSONAL_TIME)
                    .setFreeForecastCompanyCount(WebConst.FREE_RWA_FORECAST_COMPANY_TIME);
            webUserDao.saveOrUpdate(user);
        }
        String token = webLoginImpl(user);

        return new WebCommonWxLoginRes()
                .setToken(token)
                .setOpenId(openId)
                .setNewUser(newUser);
    }

    /**
     * 微信登录后绑定手机或邮箱
     */
    @Transactional(rollbackOn = Exception.class)
    public void webBindMobileEmail(WebCommonBindMobileEmailReq req, WebUser webUser) throws CommonException {
        if (StringUtils.isBlank(req.getMobile()) && StringUtils.isBlank(req.getEmail())) {
            CommonException.throwException("手机号码和邮箱不能同时为空");
        }
        if (!StringUtils.isBlank(req.getMobile()) && !StringUtils.isBlank(req.getEmail())) {
            CommonException.throwException("手机号码和邮箱只能二选一");
        }

        String nickName = null;
        String redisKey = null;
        if (StringUtils.isNotBlank(req.getMobile())) {
            redisKey = RedisKey.getMobileVerifyCode(req.getEmail());
            nickName = req.getMobile();
        }
        if (StringUtils.isNotBlank(req.getEmail())) {
            redisKey = RedisKey.getEmailVerifyCode(req.getEmail());
            nickName = req.getEmail();
        }
        // 检查验证码
        String verifyCode = redisTemplate.getIfExceptionDelete(redisKey);
        if (StringUtils.isBlank(verifyCode)) {
            CommonException.throwException("不存在验证码");
        }
        if (!verifyCode.equals(req.getVerifyCode())) {
            CommonException.throwException("验证码有误");
        }
        if (new WebUserQuery(webUserDao).email(req.getEmail()).count() > 0) {
            CommonException.throwException("邮箱已存在");
        }

        webUser.setMobile(req.getMobile())
                .setEmail(req.getEmail())
                .setNickName(nickName);

        // 推荐人
        if (req.getKolAccount() != null) {
            Kol kol = new KolQuery(kolDao)
                    .account(req.getKolAccount())
                    .single();
            if (kol != null) {
                webUser.setKolId(kol.getId());
            }
        }

        webUserDao.saveOrUpdate(webUser);
    }

    @Transactional(rollbackOn = Exception.class)
    public WebCommonEmailLoginRes webEmailLogin(WebCommonEmailLoginReq req) throws CommonException {
        // 查询账号
        WebUser user = new WebUserQuery(webUserDao)
                .email(req.getEmail())
                .single();
        if (user == null) {
            user = new WebUser()
                    .setEmail(req.getEmail())
                    .setNickName(req.getEmail())
                    .setLevel(MemberLevelEnum.会员)
                    .setExpTime(DateUtils.stringToDateWithoutException("2099-01-01 00:00:00"))
                    .setFreeChatCount(WebConst.FREE_CHAT_TIME)
                    .setFreeForecastPersonalCount(WebConst.FREE_RWA_FORECAST_PERSONAL_TIME)
                    .setFreeForecastCompanyCount(WebConst.FREE_RWA_FORECAST_COMPANY_TIME);
            // 推荐人
            if (req.getKolAccount() != null) {
                Kol kol = new KolQuery(kolDao)
                        .account(req.getKolAccount())
                        .single();
                if (kol != null) {
                    user.setKolId(kol.getId());
                }
            }
            webUserDao.saveOrUpdate(user);
        }
        String token = webLoginImpl(user);

        return new WebCommonEmailLoginRes()
                .setToken(token);
    }

    /**
     * 获取邮箱验证码
     */
    public void webSendEmailVerifyCode(WebCommonSendEmailVerityCodeReq req) {
        String verifyCode = CommonUtils.generateVerifyCode(6);
        // 5分钟之后过期
        final int minute = 5;
        emailManager.sendEmailVerifyCode(req.getEmail(), verifyCode, minute);

        redisTemplate.opsForValue().set(RedisKey.getEmailVerifyCode(req.getEmail()), verifyCode, minute, TimeUnit.MINUTES);
    }

    @Transactional(rollbackOn = Exception.class)
    public WebCommonMobileLoginRes webMobileLogin(WebCommonMobileLoginReq req) throws CommonException {
        // 查询账号
        WebUser user = new WebUserQuery(webUserDao)
                .mobile(req.getMobile())
                .single();
        if (user == null) {
            user = new WebUser()
                    .setMobile(req.getMobile())
                    .setNickName(req.getMobile())
                    .setLevel(MemberLevelEnum.会员)
                    .setExpTime(DateUtils.stringToDateWithoutException("2099-01-01 00:00:00"))
                    .setFreeChatCount(WebConst.FREE_CHAT_TIME)
                    .setFreeForecastPersonalCount(WebConst.FREE_RWA_FORECAST_PERSONAL_TIME)
                    .setFreeForecastCompanyCount(WebConst.FREE_RWA_FORECAST_COMPANY_TIME);
            // 推荐人
            if (req.getKolAccount() != null) {
                Kol kol = new KolQuery(kolDao)
                        .account(req.getKolAccount())
                        .single();
                if (kol != null) {
                    user.setKolId(kol.getId());
                }
            }
            webUserDao.saveOrUpdate(user);
        }
        String token = webLoginImpl(user);

        return new WebCommonMobileLoginRes()
                .setToken(token);
    }

    /**
     * 获取手机号码验证码
     */
    public void webSendMobileVerifyCode(WebCommonSendMobileVerityCodeReq req) throws CommonException {
        String verifyCode = CommonUtils.generateVerifyCode(6);
        verifyCode = "888888";

        // 发送短信验证码
        tencentSmsManager.sendLoginVerifyCode(req.getMobile(), verifyCode, 5);

        redisTemplate.opsForValue().set(RedisKey.getMobileVerifyCode(req.getMobile()), verifyCode, 5, TimeUnit.MINUTES);
    }

    private String webLoginImpl(WebUser user) {
        // 分配token，并且保存到redis
        String token = CommonUtils.getToken();
        RdsWebLoginInfo loginInfo = new RdsWebLoginInfo()
                .setWebUserId(user.getId());

        redisTemplate.opsForValue().set(RedisKey.getWebToken(token), loginInfo, 7, TimeUnit.DAYS);

        return token;
    }

    /**
     * 微信端换取手机号码
     */
    public WebCommonGetMobileByWxCodeRes webGetMobileByWxCode(WebCommonGetMobileByWxCodeReq req, WebUser webUser) throws CommonException {
        String mobileCode = req.getMobileCode().trim();
        WxGetUserPhoneNumberRes.PhoneInfoBean phoneInfo = wxCommonManager.getUserPhoneNumber(mobileCode);
        if (phoneInfo == null) {
            CommonException.throwException("获取手机号码失败");
        }

        return new WebCommonGetMobileByWxCodeRes()
                .setMobile(phoneInfo.getPurePhoneNumber());
    }

    /**
     * 上传文件
     */
    public String uploadFile(InputStream uploadFileInputStream, String fileName, OssPath ossPath) throws CommonException {
        String ossFileName = ossPath.getPath() + "/" + FileNameUtil.getPrefix(fileName) + "_" + System.currentTimeMillis();
        if (StringUtils.isNotBlank(FileNameUtil.extName(fileName))) {
            ossFileName += "." + FileNameUtil.extName(fileName);
        }
        byte[] buffer = IoUtil.readBytes(uploadFileInputStream, false);
        String uploadUrl = cosManager.uploadFile(buffer, ossFileName);
        CommonException.throwExceptionIfTrue(uploadUrl == null, "上传异常，请联系管理员");

        return uploadUrl;
    }


    /**
     * 获取账号信息
     */
    public WebCommonGetProfileRes webGetProfile(WebUser webUser) {
        return new WebCommonGetProfileRes()
                .setNickName(webUser.getNickName())
                .setExpTime(webUser.getExpTime())
                .setLevel(webUser.getLevel())
                .setFreeForecastPersonalCount(webUser.getFreeForecastPersonalCount())
                .setFreeForecastCompanyCount(webUser.getFreeForecastCompanyCount())
                .setFreeChatCount(webUser.getFreeChatCount());
    }

    public WebUser getUserForStream(HttpServletRequest request) throws VerifyTokenException {
        String token = request.getParameter("Authorization");
        String key = RedisKey.getWebToken(token);
        RdsWebLoginInfo rdsInfo = redisTemplate.getIfExceptionDelete(key);
        if (rdsInfo == null) {
            return null;
        }
        WebUser webUser = new WebUserQuery(webUserDao)
                .id(rdsInfo.getWebUserId())
                .single();
        return webUser;
    }
}